#include "StreamManager.h"
#include "UsbStreamHandle.h"
#include "RtspStreamHandle.h"
#include "AudioStreamHandle.h"
#include "File.h"
#include "Time.h"



std::atomic<bool> g_bInited = false;
std::atomic<bool> g_bExit = false;
std::mutex g_mtLock;
std::condition_variable g_cvCond;
std::thread g_thRun;
using namespace Stream;
static void run()
{
    bool bFirstRun = true;
    while (!g_bExit.load())
    {
        // create directory every day
        static auto generate_directory = [](FileNameType type = kFileTypeVideo) {
            switch (type)
            {
            case kFileTypePicture:
                return kPictureDir + "/" + Time::GetCurrentDate();
            default:
                return kVideoDir + "/" + Time::GetCurrentDate();
            }
        };
        if (bFirstRun)
        {
            File::CreateSingleDirectory(kVideoDir);
            File::CreateSingleDirectory(kPictureDir);
			bFirstRun = false;
        }
        File::CreateSingleDirectory(generate_directory());
        File::CreateSingleDirectory(generate_directory(kFileTypePicture));
        SHARED_LOCK(g_mtLock);
        g_cvCond.wait_for(locker, std::chrono::milliseconds(1000), []() {
            return g_bExit.load();
        });
    }
}

void StreamMangement::Init()
{
    if (!g_bInited.load()) {
        g_thRun = std::thread(run);
        bool bInited = false;
        if (g_bInited.compare_exchange_strong(bInited, true, std::memory_order_acq_rel)) {
            // it needs do this while open dshow
            avdevice_register_all();
        }
    }
}

void StreamMangement::Uninit()
{
    g_bExit.store(true);
    g_cvCond.notify_all();
    if (g_thRun.joinable())
        g_thRun.join();
}


StreamMangement::StreamMangement(StreamType nStreamType)
{
    Init();
    m_pHandle = nullptr;
    switch (nStreamType)
    {
    case kStreamTypeUsb:
        m_pHandle = std::make_shared<UsbStreamHandle>();
        break;
    case kStreamTypeAudio:
        m_pHandle = std::make_shared<AudioStreamHandle>();
        break;
    default:
        m_pHandle = std::make_shared<RtspStreamHandle>();
        break;
    }
}

StreamMangement::~StreamMangement()
{
}

void StreamMangement::ListSupportedHD()
{
    std::stringstream ss;
    enum AVHWDeviceType nType = AV_HWDEVICE_TYPE_NONE;
    while ((nType = av_hwdevice_iterate_types(nType)) != AV_HWDEVICE_TYPE_NONE)
        ss << av_hwdevice_get_type_name(nType) << ",";
    printf("Supported hard device:%s\n ", ss.str().c_str());
}

void StreamMangement::ListDshowDevice()
{
    AVFormatContext *pFormatCtx = avformat_alloc_context();
    AVDictionary* options = NULL;
    av_dict_set(&options, "list_devices", "true", 0);
    AVInputFormat *iformat = av_find_input_format("dshow");
    printf("========Device Info=============\n");
    avformat_open_input(&pFormatCtx, "video=dummy", iformat, &options);
    printf("================================\n");
    avformat_free_context(pFormatCtx);
}

bool StreamMangement::StartDecode(const StreamInfo& infoStream)
{
    VERIFY_RETURN(check_handle());
    VERIFY_RETURN(check_para(infoStream));
    return m_pHandle->StartDecode(infoStream);
}

void StreamMangement::StopDecode()
{
    VERIFY_RETURN_VOID(check_handle());
    m_pHandle->StopDecode();
    Uninit();
}

bool StreamMangement::GetRtmpUrl(std::string& strRtmp)
{
    VERIFY_RETURN(check_handle());
    m_pHandle->GetRtmpUrl(strRtmp);

    return true;
}

bool StreamMangement::check_handle()
{
    if (nullptr == m_pHandle)
    {
        printf("Invalid stream handle\n");
        return false;
    }
    return true;
}

bool StreamMangement::check_para(const StreamInfo& infoStream)
{
    if (infoStream.strInput.empty())
    {
        printf("Invalid stream input\n");
        return false;
    }
    if (!(infoStream.bRtmp || infoStream.bSavePic || infoStream.bSaveVideo))
    {
        printf("Nothing to do, save picture, save video of push rtmp\n");
        return false;
    }
    return true;
}
